// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/cookies/canonical_cookie.h"

#include <memory>

#include "base/test/histogram_tester.h"
#include "net/cookies/cookie_constants.h"
#include "net/cookies/cookie_options.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"

namespace net {

TEST(CanonicalCookieTest, Constructor)
{
    GURL url("http://www.example.com/test");
    base::Time current_time = base::Time::Now();

    CanonicalCookie cookie(url, "A", "2", "www.example.com", "/test",
        current_time, base::Time(), current_time, false, false,
        CookieSameSite::DEFAULT_MODE, COOKIE_PRIORITY_DEFAULT);
    EXPECT_EQ(url.GetOrigin(), cookie.Source());
    EXPECT_EQ("A", cookie.Name());
    EXPECT_EQ("2", cookie.Value());
    EXPECT_EQ("www.example.com", cookie.Domain());
    EXPECT_EQ("/test", cookie.Path());
    EXPECT_FALSE(cookie.IsSecure());
    EXPECT_FALSE(cookie.IsHttpOnly());
    EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie.SameSite());

    CanonicalCookie cookie2(url, "A", "2", std::string(), std::string(),
        current_time, base::Time(), current_time, false,
        false, CookieSameSite::DEFAULT_MODE,
        COOKIE_PRIORITY_DEFAULT);
    EXPECT_EQ(url.GetOrigin(), cookie.Source());
    EXPECT_EQ("A", cookie2.Name());
    EXPECT_EQ("2", cookie2.Value());
    EXPECT_EQ("", cookie2.Domain());
    EXPECT_EQ("", cookie2.Path());
    EXPECT_FALSE(cookie2.IsSecure());
    EXPECT_FALSE(cookie2.IsHttpOnly());
    EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie2.SameSite());
}

TEST(CanonicalCookieTest, Create)
{
    // Test creating cookies from a cookie string.
    GURL url("http://www.example.com/test/foo.html");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, "A=2", creation_time, options));
    EXPECT_EQ(url.GetOrigin(), cookie->Source());
    EXPECT_EQ("A", cookie->Name());
    EXPECT_EQ("2", cookie->Value());
    EXPECT_EQ("www.example.com", cookie->Domain());
    EXPECT_EQ("/test", cookie->Path());
    EXPECT_FALSE(cookie->IsSecure());

    GURL url2("http://www.foo.com");
    cookie = CanonicalCookie::Create(url2, "B=1", creation_time, options);
    EXPECT_EQ(url2.GetOrigin(), cookie->Source());
    EXPECT_EQ("B", cookie->Name());
    EXPECT_EQ("1", cookie->Value());
    EXPECT_EQ("www.foo.com", cookie->Domain());
    EXPECT_EQ("/", cookie->Path());
    EXPECT_FALSE(cookie->IsSecure());

    // Test creating secure cookies. RFC 6265 allows insecure urls to set secure
    // cookies.
    cookie = CanonicalCookie::Create(url, "A=2; Secure", creation_time, options);
    EXPECT_TRUE(cookie.get());
    EXPECT_TRUE(cookie->IsSecure());

    // Test creating http only cookies.
    cookie = CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options);
    EXPECT_FALSE(cookie.get());
    CookieOptions httponly_options;
    httponly_options.set_include_httponly();
    cookie = CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time,
        httponly_options);
    EXPECT_TRUE(cookie->IsHttpOnly());

    // Test creating SameSite cookies.
    CookieOptions same_site_options;
    same_site_options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
    cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time,
        same_site_options);
    EXPECT_TRUE(cookie.get());
    EXPECT_EQ(CookieSameSite::STRICT_MODE, cookie->SameSite());
    cookie = CanonicalCookie::Create(url, "A=2; SameSite=Lax", creation_time,
        same_site_options);

    // Test the creating cookies using specific parameter instead of a cookie
    // string.
    cookie = CanonicalCookie::Create(url, "A", "2", "www.example.com", "/test",
        creation_time, base::Time(), false, false,
        CookieSameSite::DEFAULT_MODE, false,
        COOKIE_PRIORITY_DEFAULT);
    EXPECT_EQ(url.GetOrigin(), cookie->Source());
    EXPECT_EQ("A", cookie->Name());
    EXPECT_EQ("2", cookie->Value());
    EXPECT_EQ(".www.example.com", cookie->Domain());
    EXPECT_EQ("/test", cookie->Path());
    EXPECT_FALSE(cookie->IsSecure());
    EXPECT_FALSE(cookie->IsHttpOnly());
    EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite());

    cookie = CanonicalCookie::Create(url, "A", "2", ".www.example.com", "/test",
        creation_time, base::Time(), false, false,
        CookieSameSite::DEFAULT_MODE, false,
        COOKIE_PRIORITY_DEFAULT);
    EXPECT_EQ(url.GetOrigin(), cookie->Source());
    EXPECT_EQ("A", cookie->Name());
    EXPECT_EQ("2", cookie->Value());
    EXPECT_EQ(".www.example.com", cookie->Domain());
    EXPECT_EQ("/test", cookie->Path());
    EXPECT_FALSE(cookie->IsSecure());
    EXPECT_FALSE(cookie->IsHttpOnly());
    EXPECT_EQ(CookieSameSite::NO_RESTRICTION, cookie->SameSite());
}

TEST(CanonicalCookieTest, CreateInvalidSameSite)
{
    GURL url("http://www.example.com/test/foo.html");
    base::Time now = base::Time::Now();
    std::unique_ptr<CanonicalCookie> cookie;
    CookieOptions options;

    // Invalid 'SameSite' attribute values.
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);

    cookie = CanonicalCookie::Create(url, "A=2; SameSite=Invalid", now, options);
    EXPECT_EQ(nullptr, cookie.get());

    cookie = CanonicalCookie::Create(url, "A=2; SameSite", now, options);
    EXPECT_EQ(nullptr, cookie.get());
}

TEST(CanonicalCookieTest, EmptyExpiry)
{
    GURL url("http://www7.ipdl.inpit.go.jp/Tokujitu/tjkta.ipdl?N0000=108");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    std::string cookie_line = "ACSTM=20130308043820420042; path=/; domain=ipdl.inpit.go.jp; Expires=";
    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, cookie_line, creation_time, options));
    EXPECT_TRUE(cookie.get());
    EXPECT_FALSE(cookie->IsPersistent());
    EXPECT_FALSE(cookie->IsExpired(creation_time));
    EXPECT_EQ(base::Time(), cookie->ExpiryDate());

    // With a stale server time
    options.set_server_time(creation_time - base::TimeDelta::FromHours(1));
    cookie = CanonicalCookie::Create(url, cookie_line, creation_time, options);
    EXPECT_TRUE(cookie.get());
    EXPECT_FALSE(cookie->IsPersistent());
    EXPECT_FALSE(cookie->IsExpired(creation_time));
    EXPECT_EQ(base::Time(), cookie->ExpiryDate());

    // With a future server time
    options.set_server_time(creation_time + base::TimeDelta::FromHours(1));
    cookie = CanonicalCookie::Create(url, cookie_line, creation_time, options);
    EXPECT_TRUE(cookie.get());
    EXPECT_FALSE(cookie->IsPersistent());
    EXPECT_FALSE(cookie->IsExpired(creation_time));
    EXPECT_EQ(base::Time(), cookie->ExpiryDate());
}

TEST(CanonicalCookieTest, IsEquivalent)
{
    GURL url("http://www.example.com/");
    std::string cookie_name = "A";
    std::string cookie_value = "2EDA-EF";
    std::string cookie_domain = ".www.example.com";
    std::string cookie_path = "/";
    base::Time creation_time = base::Time::Now();
    base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2);
    bool secure(false);
    bool httponly(false);
    CookieSameSite same_site(CookieSameSite::NO_RESTRICTION);

    // Test that a cookie is equivalent to itself.
    std::unique_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM));
    EXPECT_TRUE(cookie->IsEquivalent(*cookie));

    // Test that two identical cookies are equivalent.
    std::unique_ptr<CanonicalCookie> other_cookie(CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM));
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    // Tests that use different variations of attribute values that
    // DON'T affect cookie equivalence.
    other_cookie = CanonicalCookie::Create(url, cookie_name, "2", cookie_domain, cookie_path,
        creation_time, expiration_time, secure, httponly,
        same_site, false, COOKIE_PRIORITY_HIGH);
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    base::Time other_creation_time = creation_time + base::TimeDelta::FromMinutes(2);
    other_cookie = CanonicalCookie::Create(
        url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, true, httponly, same_site, false, COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, true, same_site, false, COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, CookieSameSite::STRICT_MODE, false,
        COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalent(*other_cookie));

    // Tests that use different variations of attribute values that
    // DO affect cookie equivalence.
    other_cookie = CanonicalCookie::Create(
        url, "B", cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, std::string(), cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsDomainCookie());
    EXPECT_FALSE(other_cookie->IsDomainCookie());
    EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, ".example.com", cookie_path,
        creation_time, expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_FALSE(cookie->IsEquivalent(*other_cookie));
}

TEST(CanonicalCookieTest, IsEquivalentForSecureCookieMatching)
{
    GURL url("http://www.example.com/");
    std::string cookie_name = "A";
    std::string cookie_value = "2EDA-EF";
    std::string cookie_domain = ".www.example.com";
    std::string cookie_path = "/";
    base::Time creation_time = base::Time::Now();
    base::Time expiration_time = creation_time + base::TimeDelta::FromDays(2);
    bool secure(false);
    bool httponly(false);
    CookieSameSite same_site(CookieSameSite::NO_RESTRICTION);

    // Test that a cookie is equivalent to itself.
    std::unique_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM));
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*cookie));

    // Test that two identical cookies are equivalent.
    std::unique_ptr<CanonicalCookie> other_cookie(CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM));
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    // Tests that use different variations of attribute values that
    // DON'T affect cookie equivalence. Differs from the IsEquivalent tests above
    // as follows:
    //    * Should return true even if paths differ.
    //    * Should return true if the domains "domain-match" (but are not
    //      identical).
    other_cookie = CanonicalCookie::Create(url, cookie_name, "2", cookie_domain, cookie_path,
        creation_time, expiration_time, secure, httponly,
        same_site, false, COOKIE_PRIORITY_HIGH);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    base::Time other_creation_time = creation_time + base::TimeDelta::FromMinutes(2);
    other_cookie = CanonicalCookie::Create(
        url, cookie_name, "2", cookie_domain, cookie_path, other_creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, true, httponly, same_site, false, COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, true, same_site, false, COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_name, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, CookieSameSite::STRICT_MODE, false,
        COOKIE_PRIORITY_LOW);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    // The following 3 tests' expected results differ from their IsEquivalent
    // counterparts above.
    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, cookie_domain, "/test/0", creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, std::string(), cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsDomainCookie());
    EXPECT_FALSE(other_cookie->IsDomainCookie());
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    other_cookie = CanonicalCookie::Create(
        url, cookie_name, cookie_value, ".example.com", cookie_path,
        creation_time, expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_TRUE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));

    // Tests that use different variations of attribute values that
    // DO affect cookie equivalence. Note that unlike the IsEquivalent tests
    // above, this does *not* include tests for differing paths or domains that
    // "domain-match".
    other_cookie = CanonicalCookie::Create(
        url, "B", cookie_value, cookie_domain, cookie_path, creation_time,
        expiration_time, secure, httponly, same_site, false,
        COOKIE_PRIORITY_MEDIUM);
    EXPECT_FALSE(cookie->IsEquivalentForSecureCookieMatching(*other_cookie));
}

TEST(CanonicalCookieTest, IsDomainMatch)
{
    GURL url("http://www.example.com/test/foo.html");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, "A=2", creation_time, options));
    EXPECT_TRUE(cookie->IsHostCookie());
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("foo.www.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("example.com"));

    cookie = CanonicalCookie::Create(url, "A=2; Domain=www.example.com",
        creation_time, options);
    EXPECT_TRUE(cookie->IsDomainCookie());
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_TRUE(cookie->IsDomainMatch("foo.www.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("example.com"));

    cookie = CanonicalCookie::Create(url, "A=2; Domain=.www.example.com",
        creation_time, options);
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_TRUE(cookie->IsDomainMatch("www.example.com"));
    EXPECT_TRUE(cookie->IsDomainMatch("foo.www.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("www0.example.com"));
    EXPECT_FALSE(cookie->IsDomainMatch("example.com"));
}

TEST(CanonicalCookieTest, IsOnPath)
{
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    std::unique_ptr<CanonicalCookie> cookie(CanonicalCookie::Create(
        GURL("http://www.example.com"), "A=2", creation_time, options));
    EXPECT_TRUE(cookie->IsOnPath("/"));
    EXPECT_TRUE(cookie->IsOnPath("/test"));
    EXPECT_TRUE(cookie->IsOnPath("/test/bar.html"));

    // Test the empty string edge case.
    EXPECT_FALSE(cookie->IsOnPath(std::string()));

    cookie = CanonicalCookie::Create(GURL("http://www.example.com/test/foo.html"),
        "A=2", creation_time, options);
    EXPECT_FALSE(cookie->IsOnPath("/"));
    EXPECT_TRUE(cookie->IsOnPath("/test"));
    EXPECT_TRUE(cookie->IsOnPath("/test/bar.html"));
    EXPECT_TRUE(cookie->IsOnPath("/test/sample/bar.html"));
}

TEST(CanonicalCookieTest, IncludeForRequestURL)
{
    GURL url("http://www.example.com");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, "A=2", creation_time, options));
    EXPECT_TRUE(cookie->IncludeForRequestURL(url, options));
    EXPECT_TRUE(cookie->IncludeForRequestURL(
        GURL("http://www.example.com/foo/bar"), options));
    EXPECT_TRUE(cookie->IncludeForRequestURL(
        GURL("https://www.example.com/foo/bar"), options));
    EXPECT_FALSE(
        cookie->IncludeForRequestURL(GURL("https://sub.example.com"), options));
    EXPECT_FALSE(cookie->IncludeForRequestURL(GURL("https://sub.www.example.com"),
        options));

    // Test that cookie with a cookie path that does not match the url path are
    // not included.
    cookie = CanonicalCookie::Create(url, "A=2; Path=/foo/bar", creation_time,
        options);
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
    EXPECT_TRUE(cookie->IncludeForRequestURL(
        GURL("http://www.example.com/foo/bar/index.html"), options));

    // Test that a secure cookie is not included for a non secure URL.
    GURL secure_url("https://www.example.com");
    cookie = CanonicalCookie::Create(secure_url, "A=2; Secure", creation_time,
        options);
    EXPECT_TRUE(cookie->IsSecure());
    EXPECT_TRUE(cookie->IncludeForRequestURL(secure_url, options));
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));

    // Test that http only cookies are only included if the include httponly flag
    // is set on the cookie options.
    options.set_include_httponly();
    cookie = CanonicalCookie::Create(url, "A=2; HttpOnly", creation_time, options);
    EXPECT_TRUE(cookie->IsHttpOnly());
    EXPECT_TRUE(cookie->IncludeForRequestURL(url, options));
    options.set_exclude_httponly();
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
}

TEST(CanonicalCookieTest, IncludeSameSiteForSameSiteURL)
{
    GURL url("https://example.test");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;
    std::unique_ptr<CanonicalCookie> cookie;

    // `SameSite=Strict` cookies are included for a URL only if the options'
    // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX.
    cookie = CanonicalCookie::Create(url, "A=2; SameSite=Strict", creation_time,
        options);
    EXPECT_EQ(CookieSameSite::STRICT_MODE, cookie->SameSite());
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
    EXPECT_TRUE(cookie->IncludeForRequestURL(url, options));

    // `SameSite=Lax` cookies are included for a URL only if the options'
    // SameSiteCookieMode is INCLUDE_STRICT_AND_LAX.
    cookie = CanonicalCookie::Create(url, "A=2; SameSite=Lax", creation_time, options);
    EXPECT_EQ(CookieSameSite::LAX_MODE, cookie->SameSite());
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::DO_NOT_INCLUDE);
    EXPECT_FALSE(cookie->IncludeForRequestURL(url, options));
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_LAX);
    EXPECT_TRUE(cookie->IncludeForRequestURL(url, options));
    options.set_same_site_cookie_mode(
        CookieOptions::SameSiteCookieMode::INCLUDE_STRICT_AND_LAX);
    EXPECT_TRUE(cookie->IncludeForRequestURL(url, options));
}

TEST(CanonicalCookieTest, PartialCompare)
{
    GURL url("http://www.example.com");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;
    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, "a=b", creation_time, options));
    std::unique_ptr<CanonicalCookie> cookie_different_path(
        CanonicalCookie::Create(url, "a=b; path=/foo", creation_time, options));
    std::unique_ptr<CanonicalCookie> cookie_different_value(
        CanonicalCookie::Create(url, "a=c", creation_time, options));

    // Cookie is equivalent to itself.
    EXPECT_FALSE(cookie->PartialCompare(*cookie));

    // Changing the path affects the ordering.
    EXPECT_TRUE(cookie->PartialCompare(*cookie_different_path));
    EXPECT_FALSE(cookie_different_path->PartialCompare(*cookie));

    // Changing the value does not affect the ordering.
    EXPECT_FALSE(cookie->PartialCompare(*cookie_different_value));
    EXPECT_FALSE(cookie_different_value->PartialCompare(*cookie));

    // Cookies identical for PartialCompare() are equivalent.
    EXPECT_TRUE(cookie->IsEquivalent(*cookie_different_value));
    EXPECT_TRUE(cookie->IsEquivalent(*cookie));
}

TEST(CanonicalCookieTest, FullCompare)
{
    GURL url("http://www.example.com");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;
    std::unique_ptr<CanonicalCookie> cookie(
        CanonicalCookie::Create(url, "a=b", creation_time, options));
    std::unique_ptr<CanonicalCookie> cookie_different_path(
        CanonicalCookie::Create(url, "a=b; path=/foo", creation_time, options));
    std::unique_ptr<CanonicalCookie> cookie_different_value(
        CanonicalCookie::Create(url, "a=c", creation_time, options));

    // Cookie is equivalent to itself.
    EXPECT_FALSE(cookie->FullCompare(*cookie));

    // Changing the path affects the ordering.
    EXPECT_TRUE(cookie->FullCompare(*cookie_different_path));
    EXPECT_FALSE(cookie_different_path->FullCompare(*cookie));

    // Changing the value affects the ordering.
    EXPECT_TRUE(cookie->FullCompare(*cookie_different_value));
    EXPECT_FALSE(cookie_different_value->FullCompare(*cookie));

    // FullCompare() implies PartialCompare().
    auto check_consistency =
        [](const CanonicalCookie& a, const CanonicalCookie& b) {
            if (a.FullCompare(b))
                EXPECT_FALSE(b.PartialCompare(a));
            else if (b.FullCompare(a))
                EXPECT_FALSE(a.PartialCompare(b));
        };

    check_consistency(*cookie, *cookie_different_path);
    check_consistency(*cookie, *cookie_different_value);
    check_consistency(*cookie_different_path, *cookie_different_value);
}

TEST(CanonicalCookieTest, SecureCookiePrefix)
{
    GURL https_url("https://www.example.test");
    GURL http_url("http://www.example.test");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    // A __Secure- cookie must be Secure.
    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B", creation_time,
        options));
    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B; httponly",
        creation_time, options));

    // A typoed prefix does not have to be Secure.
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__secure-A=B; Secure",
        creation_time, options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__secure-A=C;", creation_time,
        options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=B; Secure",
        creation_time, options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=C;", creation_time,
        options));

    // A __Secure- cookie can't be set on a non-secure origin.
    EXPECT_FALSE(CanonicalCookie::Create(http_url, "__Secure-A=B; Secure",
        creation_time, options));
}

TEST(CanonicalCookieTest, HostCookiePrefix)
{
    GURL https_url("https://www.example.test");
    GURL http_url("http://www.example.test");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;
    std::string domain = https_url.host();

    // A __Host- cookie must be Secure.
    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B;", creation_time,
        options));
    EXPECT_FALSE(CanonicalCookie::Create(
        https_url, "__Host-A=B; Domain=" + domain + "; Path=/;", creation_time,
        options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure;",
        creation_time, options));

    // A __Host- cookie must be set from a secure scheme.
    EXPECT_FALSE(CanonicalCookie::Create(
        http_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;",
        creation_time, options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure;",
        creation_time, options));

    // A __Host- cookie can't have a Domain.
    EXPECT_FALSE(CanonicalCookie::Create(
        https_url, "__Host-A=B; Domain=" + domain + "; Path=/; Secure;",
        creation_time, options));
    EXPECT_FALSE(CanonicalCookie::Create(
        https_url, "__Host-A=B; Domain=" + domain + "; Secure;", creation_time,
        options));

    // A __Host- cookie must have a Path of "/".
    EXPECT_FALSE(CanonicalCookie::Create(
        https_url, "__Host-A=B; Path=/foo; Secure;", creation_time, options));
    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B; Secure;",
        creation_time, options));
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Secure; Path=/;",
        creation_time, options));

    // Rules don't apply for a typoed prefix.
    EXPECT_TRUE(CanonicalCookie::Create(
        http_url, "__host-A=B; Domain=" + domain + "; Path=/; Secure;",
        creation_time, options));
    EXPECT_TRUE(CanonicalCookie::Create(
        https_url, "__HostA=B; Domain=" + domain + "; Secure;", creation_time,
        options));
}

TEST(CanonicalCookieTest, EnforceSecureCookiesRequireSecureScheme)
{
    GURL http_url("http://www.example.com");
    GURL https_url("https://www.example.com");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;
    options.set_enforce_strict_secure();

    std::unique_ptr<CanonicalCookie> http_cookie_no_secure(
        CanonicalCookie::Create(http_url, "a=b", creation_time, options));
    std::unique_ptr<CanonicalCookie> http_cookie_secure(
        CanonicalCookie::Create(http_url, "a=b; Secure", creation_time, options));
    std::unique_ptr<CanonicalCookie> https_cookie_no_secure(
        CanonicalCookie::Create(https_url, "a=b", creation_time, options));
    std::unique_ptr<CanonicalCookie> https_cookie_secure(CanonicalCookie::Create(
        https_url, "a=b; Secure", creation_time, options));

    EXPECT_TRUE(http_cookie_no_secure.get());
    EXPECT_FALSE(http_cookie_secure.get());
    EXPECT_TRUE(https_cookie_no_secure.get());
    EXPECT_TRUE(https_cookie_secure.get());

    std::unique_ptr<CanonicalCookie> http_cookie_no_secure_extended(
        CanonicalCookie::Create(
            http_url, "a", "b", "", "", creation_time, creation_time, false,
            false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
    std::unique_ptr<CanonicalCookie> http_cookie_secure_extended(
        CanonicalCookie::Create(
            http_url, "a", "b", "", "", creation_time, creation_time, true, false,
            CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
    std::unique_ptr<CanonicalCookie> https_cookie_no_secure_extended(
        CanonicalCookie::Create(
            https_url, "a", "b", "", "", creation_time, creation_time, false,
            false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));
    std::unique_ptr<CanonicalCookie> https_cookie_secure_extended(
        CanonicalCookie::Create(
            https_url, "a", "b", "", "", creation_time, creation_time, true,
            false, CookieSameSite::STRICT_MODE, true, COOKIE_PRIORITY_DEFAULT));

    EXPECT_TRUE(http_cookie_no_secure_extended.get());
    EXPECT_FALSE(http_cookie_secure_extended.get());
    EXPECT_TRUE(https_cookie_no_secure_extended.get());
    EXPECT_TRUE(https_cookie_secure_extended.get());
}

TEST(CanonicalCookieTest, TestPrefixHistograms)
{
    base::HistogramTester histograms;
    const char kCookiePrefixHistogram[] = "Cookie.CookiePrefix";
    const char kCookiePrefixBlockedHistogram[] = "Cookie.CookiePrefixBlocked";
    GURL https_url("https://www.example.test");
    base::Time creation_time = base::Time::Now();
    CookieOptions options;

    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Host-A=B;", creation_time,
        options));

    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 1);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 1);

    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Host-A=B; Path=/; Secure",
        creation_time, options));
    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 2);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 1);
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__HostA=B; Path=/; Secure",
        creation_time, options));
    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 2);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_HOST, 1);

    EXPECT_FALSE(CanonicalCookie::Create(https_url, "__Secure-A=B;",
        creation_time, options));

    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 1);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 1);
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__Secure-A=B; Path=/; Secure",
        creation_time, options));
    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 2);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 1);
    EXPECT_TRUE(CanonicalCookie::Create(https_url, "__SecureA=B; Path=/; Secure",
        creation_time, options));
    histograms.ExpectBucketCount(kCookiePrefixHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 2);
    histograms.ExpectBucketCount(kCookiePrefixBlockedHistogram,
        CanonicalCookie::COOKIE_PREFIX_SECURE, 1);
}

} // namespace net
